﻿using HalconDotNet;
using MachineVision.Core.Extensions;
using MachineVision.Core.TemplateMatch.ShapeModel;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MachineVision.Core.TemplateMatch
{
    /// <summary>
    /// 形状匹配服务
    /// </summary>
    public class ShapeModelService : BindableBase, ITemplateMatchService
    {
        public ShapeModelService()
        {
            Info = new MethodInfo()
            {
                Name = "find_shape_model",
                Description = "Find the best matches of a shape model in an image.",
                Parameters = new List<MathodParameter>
                {
                    new MathodParameter(){ Name = "Image", Description = "Input image in which the model should be found." },
                    new MathodParameter(){ Name = "ModelID", Description = "Handle of the model." },
                    new MathodParameter(){ Name = "AngleStart", Description = "Smallest rotation of the model." },
                    new MathodParameter(){ Name = "AngleExtent", Description = "Extent of the rotation angles." },
                    new MathodParameter(){ Name = "MinScore", Description = "Minimum score of the instances of the model to be found." },
                    new MathodParameter(){ Name = "NumMatches", Description = "Number of instances of the model to be found (or 0 for all matches)." },
                    new MathodParameter(){ Name = "MaxOverlap", Description = "Maximum overlap of the instances of the model to be found." },
                    new MathodParameter(){ Name = "SubPixel", Description = "Subpixel accuracy if not equal to 'none'." },
                    new MathodParameter(){ Name = "NumLevels", Description = "Number of pyramid levels used in the matching (and lowest pyramid level to use if |NumLevels| = 2)." },
                    new MathodParameter(){ Name = "Greediness", Description = "“Greediness” of the search heuristic (0: safe but slow; 1: fast but matches may be missed)." },
                    new MathodParameter(){ Name = "Row", Description = "Row coordinate of the found instances of the model." },
                    new MathodParameter(){ Name = "Column", Description = "Column coordinate of the found instances of the model." },
                    new MathodParameter(){ Name = "Angle", Description = "Rotation angle of the found instances of the model." },
                    new MathodParameter(){ Name = "Score", Description = "Score of the found instances of the model." },
                },
                Predecessors = new List<String>
                {
                    "create_shape_model",
                    "read_shape_model",
                    "set_shape_model_origin",
                }
            };

            //初始化模版参数与运行参数的默认值
            TemplateParamter = new ShapeModelInputParameter();
            RunParamter = new ShapeModelRunParameter();
            RunParamter.ApplyDefaultParameter();
            TemplateParamter.ApplyDefaultParameter();

            this.Setting = new MatchResultSetting();
            this.Roi = new RoiParameter();
        }

        private HTuple modelID;
        HTuple hv_Row = new HTuple(), hv_Column = new HTuple();
        HTuple hv_Angle = new HTuple(), hv_Score = new HTuple();

        public MethodInfo Info { get; set; }

        private ShapeModelInputParameter templateParamter;
        private ShapeModelRunParameter runParamter;
        private MatchResultSetting setting;

        public RoiParameter Roi { get; set; }

        /// <summary>
        /// 模版参数
        /// </summary>
        public ShapeModelInputParameter TemplateParamter
        {
            get { return templateParamter; }
            set { templateParamter = value; RaisePropertyChanged(); }
        }

        /// <summary>
        /// 运行参数
        /// </summary>
        public ShapeModelRunParameter RunParamter
        {
            get { return runParamter; }
            set { runParamter = value; RaisePropertyChanged(); }
        }

        /// <summary>
        /// 匹配结果显示设置
        /// </summary>
        public MatchResultSetting Setting
        {
            get { return setting; }
            set { setting = value; RaisePropertyChanged(); }
        }

        /// <summary>
        /// 生成模版
        /// </summary>
        /// <param name="hObject"></param>
        /// <returns></returns>
        public async Task CreateTemplate(HObject image, HObject hObject)
        {
            await Task.Run(() =>
            {
                //按照hObject参数 从 image 中提取一个子图像 template
                var template = image.ReducDomain(hObject).CropDomain();
                //按照子图像 template及其在view中设置的模版参数创建模版，并且把模版保存到modelID中。
                HOperatorSet.CreateShapeModel(template, 
                    TemplateParamter.NumLevels,
                    TemplateParamter.AngleStart,
                    TemplateParamter.AngleExtent,
                    TemplateParamter.AngleStep,
                    TemplateParamter.Optimization,
                    TemplateParamter.Metric,
                    TemplateParamter.Contrast,
                    TemplateParamter.MinContrast, out modelID);
            });
        }

        /// <summary>
        /// 执行
        /// </summary>
        /// <param name="image"></param>
        public MatchResult Run(HObject image)
        {
            //用于存储模板匹配的结果。
            MatchResult matchResult = new MatchResult();
            matchResult.Results = new ObservableCollection<TemplateMatchResult>();

            if (image == null)
            {
                matchResult.Message = "输入图像无效";
                return matchResult;
            }

            if (modelID == null)
            {
                matchResult.Message = "输入模版无效";
                return matchResult;
            }

            var timeSpan = SetTimeHelper.SetTimer(() =>
            {
                //调用 ReducDomain 扩展方法来减少图像处理域。Roi 是一个包含感兴趣区域坐标的参数结构体。
                var imageReduced = image.ReducDomain(Roi);

                //执行形状模型匹配，将匹配结果存储在 hv_Row, hv_Column, hv_Angle, hv_Score 变量中。
                HOperatorSet.FindShapeModel(
                    imageReduced,
                    modelID,
                    RunParamter.AngleStart,
                    RunParamter.AngleExtent,
                    RunParamter.MinScore,
                    RunParamter.NumMatches,
                    RunParamter.MaxOverlap,
                    RunParamter.SubPixel,
                    RunParamter.NumLevels,
                    RunParamter.Greediness,
                    out hv_Row, out hv_Column, out hv_Angle, out hv_Score);
            });

            //获取形状模型的轮廓。
            HOperatorSet.GetShapeModelContours(out HObject modelContours, modelID, 1);

            //for循环把结果赋值给matchResult
            for (int i = 0; i < hv_Score.Length; i++)
            {
                //会计算一个仿射变换矩阵 homMat2D，这个矩阵根据匹配的坐标位置以及角度信息描述了如何将模型的轮廓从模型坐标系转换到图像坐标系中匹配到的位置。变换后的轮廓将精确覆盖在原始图像的相应物体上。
                HOperatorSet.VectorAngleToRigid(0, 0, 0, hv_Row.DArr[i], hv_Column.DArr[i], hv_Angle.DArr[i],out HTuple homMat2D);
                //把原始模型的轮廓通过 HOperatorSet.AffineTransContourXld 函数移动到匹配到的位置，这样可以得到每个匹配实例在图像上的具体位置。
                HOperatorSet.AffineTransContourXld(modelContours, out HObject contoursAffineTrans, homMat2D);

                //先获取轮廓，再根据匹配成功的位置把轮廓挪到那个位置(上面3段haclon代码的含义)

                //将匹配信息和轮廓存储起来。
                matchResult.Results.Add(new TemplateMatchResult()
                {
                    Index = i+1,
                    Row = hv_Row.DArr[i],
                    Column = hv_Column.DArr[i],
                    Angle = hv_Angle.DArr[i],
                    Score = hv_Score.DArr[i],
                    Contours = contoursAffineTrans
                });
            }

            matchResult.TimeSpane = timeSpan;
            matchResult.Setting = Setting;
            matchResult.Message = $"{DateTime.Now}: 匹配耗时: {timeSpan} ms，匹配个数: {matchResult.Results.Count}";

            if (matchResult.Results.Count > 0)
            {
                matchResult.IsSuccess = true;
            }

            //返回结果
            return matchResult;
        }

    }


}
